/**
 * 
 */
package pers.vic.readlog.log;

import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import pers.vic.readlog.constant.ReadlogConstant;
import pers.vic.readlog.log.model.LineAndPosition;
import pers.vic.readlog.log.util.LogUtils;

/**
 * 日志实时通信
 * 
 * @author Vic.xu
 */
@ServerEndpoint(value = ReadlogConstant.SOCKET_URL)
@Component
public class LogSocket {

	private static Logger log = LoggerFactory.getLogger(LogSocket.class);

	/**
	 * 文件路径--->Session
	 */
	private static Map<String, Set<Session>> FILE_SESSION_MAP = new ConcurrentHashMap<>();
	
	/**
	 * sessionID + 文件路径 --> 文件读取的信息
	 */
	private static Map<String, LineAndPosition> SESSION_FILEINFO_MAP = new ConcurrentHashMap<>();

	/** 记录当前在线连接数 */
	public static AtomicInteger onlineCount = new AtomicInteger(0);

	/**
	 * 连接建立成功调用的方法
	 */
	@OnOpen
	public void onOpen(Session session) {
		onlineCount.incrementAndGet(); // 在线数加1
		log.info("有新连接加入：{}，当前总数为：{}", session.getId(), onlineCount.get());
	}


	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose(Session session) {
		onlineCount.decrementAndGet(); // 在线数减1
		log.info("有一连接关闭：{}，当前总数为：{}", session.getId(), onlineCount.get());
		// 删除连接
		removeSession(session);
	}
	
	/**
	 * 关闭session的时候删除记录的对应信息
	 * @param session
	 */
	private static void removeSession(Session session) {
		String id = session.getId();
		//文件路径--->Session
		Iterator<String> iterator = FILE_SESSION_MAP.keySet().iterator();
		while(iterator.hasNext()) {
			String key = iterator.next();
			Set<Session> val = FILE_SESSION_MAP.get(key);
			Iterator<Session> iteratorVal = val.iterator();
			while(iteratorVal.hasNext()) {
				Session next = iteratorVal.next();
				if(id.equals(next.getId())) {
					iteratorVal.remove();
					break;
				}
			}
			//如果文件对应的session已经为空 则删除这个key
			if(val.isEmpty()) {
				iterator.remove();
			}
			
		}
		//sessionID + 文件路径 --> 文件读取的信息
		Iterator<String> iterator2 = SESSION_FILEINFO_MAP.keySet().iterator();
		while (iterator2.hasNext()) {
			String key = iterator2.next();
			if(key.startsWith(id)) {
				iterator2.remove();
			}
		}
		
	}

	/**
	 * 收到客户端消息后调用的方法
	 *
	 * @param message 客户端发送过来的消息：  这里只会发送 （目录 ; 文件) 
	 *   
	 */
	@OnMessage
	public void onMessage(String message, Session session) {
		log.info("服务端收到客户端[{}]的消息:{}", session.getId(), message);
		if(message == null || message.split(";").length != 2) {
			return;
		}
		File file = new File( message.split(";")[0],  message.split(";")[1]);
		if(!file.exists()) {
			return;
		}
		String path = file.getAbsolutePath();
		putToFileSessionMap(path, session);
		//计算应该从哪一行开始发送给前端；计算完毕之后开始发送
		LineAndPosition lineAndPosition = LogUtils.calcPosition(file);
		readThenSend(file, lineAndPosition, session);
		putToSessionFileInfoMap(getKey(session, path), lineAndPosition);
		
	}
	
	public static void putToFileSessionMap(String key, Session session) {
		Set<Session> set = FILE_SESSION_MAP.get(key);
		if(set == null) {
			set = new HashSet<Session>();
			FILE_SESSION_MAP.put(key, set);
		}
		set.add(session);
	}
	
	public static void putToSessionFileInfoMap(String key, LineAndPosition info) {
		SESSION_FILEINFO_MAP.put(key, info);
	}
	
	private static void readThenSend(File file, LineAndPosition lineAndPosition, Session session) {
		if(!file.exists() || lineAndPosition == null || session == null) {
			return;
		}
		if(!session.isOpen()) {
			
			return;
		}
		
		String logs = LogUtils.redLog(file, lineAndPosition);
		if(StringUtils.isBlank(logs)) {
			return;
		}
		sendMessage(logs, session);
	}

	@OnError
	public void onError(Session session, Throwable error) {
		log.error("发生错误");
		error.printStackTrace();
	}

	/**
	 * 服务端发送消息给客户端
	 */
	private static boolean sendMessage(String message, Session toSession) {
		if (!toSession.isOpen()) {
			return false;
		}
		try {
			toSession.getBasicRemote().sendText(message);
			return true;
		} catch (Exception e) {
			log.error("服务端发送消息给客户端失败：{}", e);
		}
		return false;
	}
	
	/**
	 * 当文件发生变化的时候考虑是否发送给前端
	 */
	public static void whenFileChange(File file) {
		String path = file.getAbsolutePath();
		Set<Session> list = FILE_SESSION_MAP.get(path);
		if(CollectionUtils.isEmpty(list)) {
			return;
		}
		
		for(Session session :  list) {
			String key = getKey(session, path);
			LineAndPosition lineAndPosition = SESSION_FILEINFO_MAP.get(key);
			readThenSend(file, lineAndPosition, session);
		}
		
	}
	
	private static String getKey(Session session, String path) {
		return session.getId() + "-" + path;
	}

}
